package com.leo.auth.config;

import cn.hutool.crypto.SecureUtil;
import com.leo.common.core.constant.SecurityConstants;
import com.leo.common.security.server.enhancer.JwtUserTokenEnhancer;
import com.leo.common.security.server.handler.ClientDetailsService;
import com.leo.common.security.server.handler.CustomWebResponseExceptionTranslator;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import javax.sql.DataSource;
import java.security.KeyPair;

/**
 * <p>
 * 认证服务器配置
 * </p>
 *
 * @author ：Leo
 * @since ：2021-04-06 11:43
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerJwtConfig extends AuthorizationServerConfigurerAdapter {

	private final DataSource dataSource;
	private final PasswordEncoder passwordEncoder;
	private final UserDetailsService userDetailsService;
	private final AuthenticationManager authenticationManager;

	@Bean
	@ConditionalOnMissingBean(ClientDetailsService.class)
	ClientDetailsService myClientDetailsService() {
		ClientDetailsService clientDetailsService = new ClientDetailsService(dataSource);
		clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
		clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
		return clientDetailsService;
	}

	@Bean
	@Primary
	public DefaultTokenServices tokenServices() {
		final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
		defaultTokenServices.setTokenStore(tokenStore());
		defaultTokenServices.setSupportRefreshToken(true);
		defaultTokenServices.setReuseRefreshToken(false);
		defaultTokenServices.setClientDetailsService(myClientDetailsService());
		defaultTokenServices.setTokenEnhancer(accessTokenConverter());
		return defaultTokenServices;
	}

	@Bean
	public TokenStore tokenStore() {
		return new JwtTokenStore(accessTokenConverter());
	}

	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter accessTokenConverter = new JwtUserTokenEnhancer();
		//1、对称加密，授权服务器和资源服务器使用相同的秘钥签名和验签（MAC）
		//2、非对称加密，授权服务器使用私钥签名，资源服务器使用公钥验签（RSA），这里使用RSA加密
		accessTokenConverter.setKeyPair(keyPair());
		return accessTokenConverter;
	}

	/**
	 * 从classpath下的密钥库中获取密钥对(公钥+私钥)
	 * @return KeyPair 秘钥对
	 */
	@Bean
	public KeyPair keyPair() {
		KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("pet_union.jks"), "123456".toCharArray());
		return factory.getKeyPair("pet_union", "123456".toCharArray());
	}

	/**
	 * 配置客户端信息和授权模式
	 * @param clients ClientDetailsServiceConfigurer
	 */
	@Override
	@SneakyThrows
	public void configure(ClientDetailsServiceConfigurer clients) {
		clients.withClientDetails(myClientDetailsService());
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
		oauthServer.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()")
				.checkTokenAccess("isAuthenticated()");
	}

	/**
	 * 配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)
	 * @param endpoints AuthorizationServerEndpointsConfigurer
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
				.authenticationManager(authenticationManager)
				// 该字段设置设置refresh token是否重复使用
				.pathMapping("/oauth/confirm_access", "/token/confirm_access")
				.exceptionTranslator(webResponseExceptionTranslator()).tokenServices(tokenServices());
	}

	@Bean
	public WebResponseExceptionTranslator<OAuth2Exception> webResponseExceptionTranslator() {
		return new CustomWebResponseExceptionTranslator();
	}

}
